#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
autor : Filipe Vieira
objectivos :
Gerador de numeros pseudo aleatórios
Este pequeno programa, implementa dois blocos de código semelhantes (gerador de numeros) mas baseado nas linguagens Python e C. O interface das rotinas C com Python são efetudas
 recorrendo usando ctypes.
A biblioteca  libtrab.so.1.0 é gerada usando a script genlib.sh e baseia-se nas informações de: http://blog.prashanthellina.com/2008/01/07/interfacing-python-with-c-using-ctypes/

Outputs:
O output resultante é uma lista dos numeros gerados pelo algoritmo em Python, segundo-se de uma lista de numero gerados pelo algoritmo em C. Em cada caso, são cronometados os tempos e apresentados no final. Entre cada output é realizado uma pausa de 5 segundos.

Parametros:
gerador [<totnum>]

	totnum - total de numeros pseudo-aleatórios a serem gerados.




Resultados obtidos:

(SEM TESTE DE CONVERGENCIA - função conv() )
Tnum	Totnum	Python	C	Equipamento
1	150000	2.75s	2.91s	Toshiba portege i7-2620M 2.7Ghz (Mint VM)
2	100000	1.77s	1.90s  	Toshiba portege i7-2620M 2.7Ghz (Mint VM)
3	 50000	0.91s	0.90s	Toshiba portege i7-2620M 2.7Ghz (Mint VM)
4	 25000	0.42s	0.43s	Toshiba portege i7-2620M 2.7Ghz (Mint VM)
5	 10000	0.15s	0.15s	Toshiba portege i7-2620M 2.7Ghz (Mint VM)

(COM TESTE DE CONVERGENCIA - função conv() )
6	 50000	47.88s	0.72s	Toshiba portege i7-2620M 2.7Ghz (Mint VM)
7	 25000	16.89s	0.43s	Toshiba portege i7-2620M 2.7Ghz (Mint VM)
8	 10000	8.40s	0.16s	Toshiba portege i7-2620M 2.7Ghz (Mint VM)


'''

import random
import time
import sys
from ctypes import *
# load the shared object
libtest = cdll.LoadLibrary('./libtrab.so.1.0')


def conv(T):    
    ''' 
    Função que verifica se a matriz T (aleatória) converge numa matriz ordenada
    Consiste na analise de sequencias de numeros na matriz T.
    O teste de convergencia é positivo quando na matriz existem
    quatro posições seguidas com o mesmo valor. O facto de serem testadas quatro
    posições tem a haver com o calculo de z1..z4 e a possibilidade de destes resultarem
    em situações de excepção, como por exemplo:
     z1=9 z2=9 z3=9 z4=9  T[0]=9  T[999]=9  N=39321
  
    Entradas: matriz T de 1000 posições
    Saidas: inteiro (0-sem convergencia 1-convergente)
    '''
    flag = 0
    i = 0
    while ((i < 997) & (flag == 0)):
        if ((T[i] == T[i+1])&(T[i+1] == T[i+2])&(T[i+2] == T[i+3])):
            flag = 1
            pass
        i += 1
        pass
    return flag

def preencher_tabela(T):
    ''' 
    Função que efectua o preenchimento de uma matriz
    com valores aleatórios de 0 a 9. Esta função implementa
    validação para que #{0} = #{1} = ... = #{9}.
 
    Entrada: array de 1000 posições
    Saida: contagem por numero de 0 a 9
    '''
    tab_contagem = [0 for x in range(0,10)]
    i = 0
    while i < 1000:
        x = random.randint(0,9)  
        if tab_contagem[x] < 100:
            tab_contagem[x] += 1
            T[i] = x
            i += 1
        pass
    return 
##tab_contagem    

def rand2(T, totnums):
    ''' 
        Função que implemeta o algoritmo de geração de numeros pseudo-aletatórios
        apresentado no enunciado do trabalho proposto.
    
        Entrada: nenhuma
        Saida: T (array de numeros)
    '''
    p = random.randint(0,999) 
    for i in range(0,totnums):

	''' 
	Solução proposta para resolver o problema de convergencia dos
	valores da matriz T.
	Por tornar impossivel a comparação dos algoritmos em C e Python,
	foi comentada.
	'''
	##while (conv(T) == 1):
	##    preencher_tabela(T)
   	##    pass

        if p>=1000:
            p = 0;
            pass
        l = p
        T[l], T[0] = T[0], T[l]
        z1 = T[0]

        if (p<999):
            l = p + 1
        else:
            l = 0
        T[l], T[0] = T[0], T[l]
        z2 = T[0]

        if (p<998):
            l = p + 2
        else:
            l = 0
        T[l], T[0] = T[0], T[l]
        z3 = T[0]

        if (p<997):
            l = p + 3
        else:
            l = 0
        T[l], T[0] = T[0], T[l]
        z4 = T[0]

        N = z1 << 12 | z2 << 8 | z3 << 4 | z4
        p = z1 * 100 + z2 * 10 + z3 * 1 +1
        print N
        pass
    return



'''
    Tabela de numeros aleatórios de 0 a 9
    1000 posições
    Utilizada para calculo de z1..z4 e N
'''
Tp = [0 for x in range(0,1000)]

# verificação de existencia de paramentros na linha de comandos
if (len(sys.argv) > 1):
    totnums = int(sys.argv[1])
else:
    totnums = 10000
    pass
print totnums

random.seed

# Chamada das funções em Python

t1 = time.time()
##contagem = 
preencher_tabela(Tp)
t2 = time.time()
rand2(Tp, totnums)
t3 = time.time()
print "---"
time.sleep(5)

# Chamada das funcoes em C
# Para chamar a funcoes feitas em C e necessario criar uma matriz T
# para os valores aleatorios de 0 a 9
IntArray = c_uint * 1000
Tc = IntArray()
tc1 = time.time()
libtest.preencher_tabela(byref(Tc))
tc2 = time.time()
libtest.rand2(byref(Tc),totnums)
tc3 = time.time()


print "(python) tempo preenchimento de T:{0}".format(t2-t1)
print "(python) tempo algoritmo:{0}".format(t3-t2)
print "(C) tempo preenchimento de T:{0}".format(tc2-tc1)
print "(C) tempo algoritmo:{0}".format(tc3-tc2)
print totnums

##print len(T)
##print T
##contagem(T)
